/*
 * opcodes.c -- part of ZilUtils/ZilAsm
 *
 * Copyright (C) 2016 Jason Self <j@jxself.org>
 *
 * Based on ZILF (c) 2010, 2015 Jesse McGrew
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#include <assert.h>

#include "opcodes.h"

typedef struct {
	unsigned opcode;
	const char *classic_name;
	const char *inform_name;
	int minver;
	int maxver;
	ZOpcode_flags flags;
} Opcode_detailed_info;

static Opcode_detailed_info detailed_opcodes[] = {
	{ 20,  "ADD",         "add",             1, 6, Zop_store   },              // Add
	{ 259, "ASHIFT",      "art_shift",       5, 6, Zop_store   },              // Ashift
	{ 255, "ASSIGNED?",   "check_arg_count", 5, 6, Zop_branch | Zop_indvar },  // Assigned_P
	{ 9,   "BAND",        "and",             1, 6, Zop_store   },              // Band
	{ 143, "BCOM",        "not",             1, 4, Zop_store   },              // Bcom_V1
	{ 248, "BCOM",        "not",             5, 6, Zop_store   },              // Bcom_V5
	{ 8,   "BOR",         "or",              1, 6, Zop_store   },              // Bor
	{ 7,   "BTST",        "test",            1, 6, Zop_branch  },              // Btst
	{ 242, "BUFOUT",      "buffer_mode",     4, 6, 0           },              // Bufout
	{ 224, "CALL",        "call_vs",         1, 6, Zop_store | Zop_call },     // Call
	{ 136, "CALL1",       "call_1s",         4, 6, Zop_store | Zop_call },     // Call1
	{ 25,  "CALL2",       "call_2s",         4, 6, Zop_store | Zop_call },     // Call2
	{ 185, "CATCH",       "catch",           5, 6, Zop_store   },              // Catch
	{ 268, "CHECKU",      "check_unicode",   5, 6, Zop_store   },              // Checku
	{ 237, "CLEAR",       "erase_window",    4, 6, 0           },              // Clear
	{ 27,  "COLOR",       "set_colour",      5, 5, 0           },              // Color_v5
	{ 27,  "COLOR",       "set_colour",      6, 6, Zop_varargs },              // Color_v6
	{ 253, "COPYT",       "copy_table",      5, 6, 0           },              // Copyt
	{ 187, "CRLF",        "new_line",        1, 6, 0           },              // Crlf
	{ 240, "CURGET",      "get_cursor",      4, 6, 0           },              // Curget
	{ 239, "CURSET",      "set_cursor",      4, 6, 0           },              // Curset
	{ 263, "DCLEAR",      "erase_picture",   6, 6, 0           },              // Dclear
	{ 134, "DEC",         "dec",             1, 6, Zop_indvar  },              // Dec
	{ 244, "DIRIN",       "input_stream",    3, 6, 0           },              // Dir-In
	{ 243, "DIROUT",      "output_stream",   3, 6, 0           },              // Dir-Out
	{ 261, "DISPLAY",     "draw_picture",    6, 6, 0           },              // Display
	{ 23,  "DIV",         "div",             1, 6, Zop_store   },              // Div
	{ 4,   "DLESS?",      "dec_chk",         1, 6, Zop_branch | Zop_indvar },  // Dless_P
//	{ 271, "ENDMOVE",     "ENDMOVE",         5, 6, Zop_store   },              // Endmove = 271
	{ 1,   "EQUAL?",      "jeq",             1, 6, Zop_branch | Zop_varargs }, // Equal_P
	{ 238, "ERASE",       "erase_line",      4, 6, 0           },              // Erase
	{ 12,  "FCLEAR",      "clear_attr",      1, 6, 0           },              // Fclear
	{ 130, "FIRST?",      "get_child",       1, 6, Zop_store | Zop_branch },   // First_P
	{ 260, "FONT",        "set_font",        5, 6, Zop_store   },              // Font
	{ 11,  "FSET",        "set_attr",        1, 6, 0           },              // Fset
	{ 10,  "FSET?",       "test_attr",       1, 6, Zop_branch  },              // Fset_P
	{ 185, "FSTACK",      "pop",             1, 4, 0           },              // Fstack_V1
	{ 277, "FSTACK",      "pop_stack",       6, 6, 0           },              // Fstack_V6
	{ 15,  "GET",         "loadw",           1, 6, Zop_store   },              // Get
	{ 16,  "GETB",        "loadb",           1, 6, Zop_store   },              // Getb
	{ 17,  "GETP",        "get_prop",        1, 6, Zop_store   },              // Getp
	{ 18,  "GETPT",       "get_prop_addr",   1, 6, Zop_store   },              // Getpt
	{ 3,   "GRTR?",       "jg",              1, 6, Zop_branch  },              // Grtr_P
	{ 241, "HLIGHT",      "set_text_style",  4, 6, 0           },              // Hlight
	{ 249, "ICALL",       "call_vn",         5, 6, Zop_call    },              // Icall
	{ 143, "ICALL1",      "call_1n",         5, 6, Zop_call    },              // Icall1
	{ 26,  "ICALL2",      "call_2n",         5, 6, Zop_call    },              // Icall2
	{ 5,   "IGRTR?",      "inc_chk",         1, 6, Zop_branch | Zop_indvar },  // Igrtr_P
	{ 6,   "IN?",         "jin",             1, 6, Zop_branch  },              // In_P
	{ 133, "INC",         "inc",             1, 6, Zop_indvar  },              // Inc
	{ 246, "INPUT",       "read_char",       4, 6, Zop_store   },              // Input
	{ 247, "INTBL?",      "scan_table",      4, 6, Zop_store | Zop_branch },   // Intbl_P
	{ 266, "IRESTORE",    "restore_undo",    5, 6, Zop_store   },              // Irestore
	{ 265, "ISAVE",       "save_undo",       5, 6, Zop_store   },              // Isave
	{ 250, "IXCALL",      "call_vn2",        5, 6, Zop_extra | Zop_call },     // Ixcall
	{ 140, "JUMP",        "jump",            1, 6, Zop_label | Zop_term },     // Jump
	{ 2,   "LESS?",       "jl",              1, 6, Zop_branch  },              // Less_P
	{ 251, "LEX",         "tokenise",        5, 6, 0           },              // Lex
	{ 131, "LOC",         "get_parent",      1, 6, Zop_store   },              // Loc
	{ 264, "MARGIN",      "set_margins",     6, 6, 0           },              // Margin
	{ 283, "MENU",        "make_menu",       6, 6, Zop_branch  },              // Menu
	{ 24,  "MOD",         "mod",             1, 6, Zop_store   },              // Mod
	{ 278, "MOUSE-INFO",  "read_mouse",      6, 6, 0           },              // MouseInfo
	{ 279, "MOUSE-LIMIT", "mouse_window",    6, 6, 0           },              // MouseLimit
	{ 14,  "MOVE",        "insert_obj",      1, 6, 0           },              // Move
	{ 22,  "MUL",         "mul",             1, 6, Zop_store   },              // Mul
	{ 129, "NEXT?",       "get_sibling",     1, 6, Zop_store | Zop_branch },   // Next_P
	{ 19,  "NEXTP",       "get_next_prop",   1, 6, Zop_store   },              // Nextp
	{ 180, "NOOP",        "nop",             1, 6, 0           },              // Noop
	{ 191, "ORIGINAL?",   "piracy",          5, 6, Zop_branch  },              // Original_P
	{ 262, "PICINF",      "picture_data",    6, 6, Zop_branch  },              // Picinf
	{ 284, "PICSET",      "picture_table",   6, 6, 0           },              // Picset
	{ 233, "POP",         "pull",            1, 5, 0           },              // Pop_v1
	{ 233, "POP",         "pull",            6, 6, Zop_store   },              // Pop_v6
	{ 141, "PRINT",       "print_paddr",     1, 6, 0           },              // Print
	{ 135, "PRINTB",      "print_addr",      1, 6, 0           },              // Printb
	{ 229, "PRINTC",      "print_char",      1, 6, 0           },              // Printc
	{ 138, "PRINTD",      "print_obj",       1, 6, 0           },              // Printd
	{ 282, "PRINTF",      "print_form",      6, 6, 0           },              // Printf
	{ 178, "PRINTI",      "print",           1, 6, Zop_string  },              // Printi
//	{ 267, "PRINTMOVE",   "PRINTMOVE",       5, 6, Zop_store   },              // Printmove
	{ 230, "PRINTN",      "print_num",       1, 6, 0           },              // Printn
	{ 179, "PRINTR",      "print_ret",       1, 6, Zop_string | Zop_term },    // Printr
	{ 254, "PRINTT",      "print_table",     5, 6, 0           },              // Printt
	{ 267, "PRINTU",      "print_unicode",   5, 6, 0           },              // Printu
	{ 132, "PTSIZE",      "get_prop_len",    1, 6, Zop_store   },              // Ptsize
	{ 232, "PUSH",        "push",            1, 6, 0           },              // Push
	{ 225, "PUT",         "storew",          1, 6, 0           },              // Put
	{ 226, "PUTB",        "storeb",          1, 6, 0           },              // Putb
	{ 227, "PUTP",        "put_prop",        1, 6, 0           },              // Putp
	{ 186, "QUIT",        "quit",            1, 6, Zop_term    },              // Quit
	{ 231, "RANDOM",      "random",          1, 6, Zop_store   },              // Random
	{ 228, "READ",        "sread",           1, 4, 0           },              // Read_v1
	{ 228, "READ",        "aread",           5, 6, Zop_store   },              // Read_v5
	{ 137, "REMOVE",      "remove_obj",      1, 6, 0           },              // Remove
	{ 183, "RESTART",     "restart",         1, 6, Zop_term    },              // Restart
	{ 182, "RESTORE",     "restore",         1, 3, Zop_branch  },              // Restore_v1
	{ 182, "RESTORE",     "restore",         4, 4, Zop_store   },              // Restore_v4
	{ 257, "RESTORE",     "restore",         5, 6, Zop_store   },              // Restore_V5
	{ 139, "RETURN",      "ret",             1, 6, Zop_term    },              // Return
	{ 177, "RFALSE",      "rfalse",          1, 6, Zop_term    },              // Rfalse
	{ 184, "RSTACK",      "ret_popped",      1, 6, Zop_term    },              // Rstack
//	{ 268, "RTIME",       "RTIME",           5, 6, Zop_store   },              // Rtime
	{ 176, "RTRUE",       "rtrue",           1, 6, Zop_term    },              // Rtrue
	{ 181, "SAVE",        "save",            1, 3, Zop_branch  },              // Save_v1
	{ 181, "SAVE",        "save",            4, 4, Zop_store   },              // Save_v4
	{ 256, "SAVE",        "save",            5, 6, Zop_store   },              // Save_V5
	{ 235, "SCREEN",      "set_window",      3, 6, 0           },              // Screen
	{ 276, "SCROLL",      "scroll_window",   6, 6, 0           },              // Scroll
//	{ 269, "SEND",        "SEND",            5, 6, Zop_store   },              // Send
	{ 270, "SERVER",      "SERVER",          5, 6, Zop_store   },              // Server
	{ 13,  "SET",         "store",           1, 6, Zop_indvar  },              // Set
	{ 258, "SHIFT",       "log_shift",       5, 6, Zop_store   },              // Shift
	{ 245, "SOUND",       "sound_effect",    3, 6, 0           },              // Sound
	{ 234, "SPLIT",       "split_window",    3, 6, 0           },              // Split
	{ 21,  "SUB",         "sub",             1, 6, Zop_store   },              // Sub
	{ 28,  "THROW",       "throw",           5, 6, Zop_term    },              // Throw
	{ 188, "USL",         "show_status",     1, 3, 0           },              // Usl
	{ 142, "VALUE",       "load",            1, 6, Zop_store | Zop_indvar },   // Value
	{ 189, "VERIFY",      "verify",          3, 6, Zop_branch  },              // Verify
	{ 274, "WINATTR",     "window_style",    6, 6, 0           },              // Winattr
	{ 275, "WINGET",      "get_wind_prop",   6, 6, Zop_store   },              // Winget
	{ 272, "WINPOS",      "move_window",     6, 6, 0           },              // Winpos
	{ 281, "WINPUT",      "put_wind_prop",   6, 6, 0           },              // Winput
	{ 273, "WINSIZE",     "window_size",     6, 6, 0           },              // Winsize
	{ 236, "XCALL",       "call_vs2",        4, 6, Zop_store | Zop_extra | Zop_call }, // Xcall
	{ 280, "XPUSH",       "push_stack",      6, 6, Zop_branch  },              // Xpush
	{ 128, "ZERO?",       "jz",              1, 6, Zop_branch  },              // Zero_P
	{ 252, "ZWSTR",       "encode_text",     5, 6, 0           }               // Zwstr
};

Symtable *Opcodes;

#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))

void init_opcodes(int version, int inform_syntax)
{
	const unsigned maxnamelen = 16;
	int n;
	if (Opcodes) symtable_destroy(Opcodes);	
	Opcodes = symtable_create(2 * ARRAY_SIZE(detailed_opcodes), maxnamelen, sizeof(ZOpcode));
	assert(Opcodes);
	for(n = 0; n < ARRAY_SIZE(detailed_opcodes); n++) {
		Opcode_detailed_info *p = &detailed_opcodes[n];
		if (version < p->minver) continue;
		if (version > p->maxver) continue;
		ZOpcode q = { p->opcode, p->flags };
		symtable_add(Opcodes, inform_syntax ? p->inform_name : p->classic_name, &q);
	}
}
